﻿#if UNITY_EDITOR

using UnityEditor;
using UnityEngine;

namespace AI.Graph.Editor {


    [CustomEditor (typeof (State), true)]
    public class StateView : CollapsibleNodeView<State> {

        private float expandedBottomAnimationValue;
        private float activeStateValue;


        private bool expandedAreaFullyClosed {
            get { return Mathf.Approximately (expandedBottomAnimationValue, 0); }
        }

        private bool drawExpandedPorts {
            get { return expandedBottomAnimationValue > 0.5F; }
        }


        protected override void OnViewCreated () {
            base.OnViewCreated ();

            if (node.isBottomExpanded) {
                expandedBottomAnimationValue = 1;
            }
        }


        protected sealed override Vector2 OnDrawNode (bool drawSimple) {
            if (drawSimple) {
                Rect topRect = DrawNodeHeader (0);

                Rect progressRect = DrawActiveStateAnimation (topRect.yMax, node.activeTimer.isActive, ref activeStateValue);

                Rect portAreaRect = DrawNodePortArea (progressRect.yMax, new[] {node.input}, new[] {node.output});

                Rect expandedRect = DrawExpandedBottom (portAreaRect.yMax + Styles.footerHeight);

                Rect footerRect = DrawStateFooter (portAreaRect.yMax);

                UpdateHiddenSubNodes (!drawExpandedPorts);

                return new Vector2 (180, footerRect.yMax + expandedRect.height);
            }
            else {
                Rect topRect = DrawNodeHeaderWithToggle (0, ref node.isCollapsed);

                Rect progressRect = DrawActiveStateAnimation (topRect.yMax, node.activeTimer.isActive, ref activeStateValue);

                Rect portAreaRect = DrawNodePortArea (progressRect.yMax, new[] {node.input}, new[] {node.output});

                Rect collapseRect = DrawNodeCollapseArea (portAreaRect.yMax);

                Rect expandedRect = DrawExpandedBottom (collapseRect.yMax + Styles.footerHeight);

                Rect footerRect = DrawStateFooter (collapseRect.yMax);

                UpdateHiddenSubNodes (!drawExpandedPorts);

                return new Vector2 (300, footerRect.yMax + expandedRect.height);
            }
        }


        /// <summary>
        /// Draws the footer with a toggle button.
        /// </summary>
        /// <param name="y">the start point.</param>
        /// <returns>Returns the size of the draw area.</returns>
        Rect DrawStateFooter (float y) {
            Rect rect = DrawNodeFooter (y);
            Rect buttonRect = new Rect (rect.center - StateStyles.footerButtonSize / 2, StateStyles.footerButtonSize);

            int id = GUIUtility.GetControlID ("AI.Graph.Editor.StateView.FooterToggle".GetHashCode (), FocusType.Passive, buttonRect);
            switch (Event.current.GetTypeForControl (id)) {
                case EventType.MouseDown:

                    if (Event.current.button == 0 && Event.current.IsMouseInsideRect (buttonRect)) {
                        GUIUtility.keyboardControl = id;
                        GUIUtility.hotControl = id;

                        node.isBottomExpanded = !node.isBottomExpanded;
                        Event.current.Use ();
                    }

                    break;

                case EventType.MouseUp:
                    if (GUIUtility.hotControl == id && Event.current.button == 0) {
                        GUIUtility.hotControl = 0;
                        GUIUtility.keyboardControl = 0;

                        Event.current.Use ();
                    }

                    break;

                case EventType.Repaint:

                    Rect buttonIconRect = new Rect (buttonRect.center - StateStyles.footerButtonIconSize / 2, StateStyles.footerButtonIconSize);

                    if (node.rect.Contains (mousePositionOnNode)) {
                        if (Event.current.IsMouseInsideRect (buttonRect)) {
                            EditorGUI.DrawSolidTexture (buttonRect, StateStyles.expandedButtonBackgroundColor.color, new BorderRadius (2, 2, 2, 2));
                        }

                        EditorGUI.DrawTexture (buttonIconRect, node.isBottomExpanded ? StateStyles.expandibleFooterButtonIconOn : StateStyles.expandibleFooterButtonIconOff, ScaleMode.ScaleToFit, StateStyles.expandedButtonIconColor.color);
                    }
                    else if (expandedAreaFullyClosed) {
                        float labelY = y + rect.height / 2 - Styles.footerHeight / 2;

                        Rect enterPortLabelRect = new Rect (node.onEnterOutput.position.x - node.position.x + node.onEnterOutput.size.x / 2 - Styles.portLabelWidth / 2, labelY, Styles.portLabelWidth, Styles.footerHeight);
                        Rect updatePortLabelRect = new Rect (node.onUpdateOutput.position.x - node.position.x + node.onUpdateOutput.size.x / 2 - Styles.portLabelWidth / 2, labelY, Styles.portLabelWidth, Styles.footerHeight);
                        Rect exitPortLabelRect = new Rect (node.onExitOutput.position.x - node.position.x + node.onExitOutput.size.x / 2 - Styles.portLabelWidth / 2, labelY, Styles.portLabelWidth, Styles.footerHeight);

                        EditorGUI.Label (enterPortLabelRect, node.onEnterOutput.name, node.onEnterOutput.hasConnections ? StateStyles.portLabelStyleOnBottom : StateStyles.portLabelStyleOnBottomNoConnections);
                        EditorGUI.Label (updatePortLabelRect, node.onUpdateOutput.name, node.onUpdateOutput.hasConnections ? StateStyles.portLabelStyleOnBottom : StateStyles.portLabelStyleOnBottomNoConnections);
                        EditorGUI.Label (exitPortLabelRect, node.onExitOutput.name, node.onExitOutput.hasConnections ? StateStyles.portLabelStyleOnBottom : StateStyles.portLabelStyleOnBottomNoConnections);
                    }

                    break;
            }

            return rect;
        }


        /// <summary>
        /// Draws an expanded footer with all sub connection ports.
        /// </summary>
        /// <param name="y">The start point.</param>
        /// <returns>Returns the size of the draw area.</returns>
        Rect DrawExpandedBottom (float y) {
            float borderRadius = 4;
            expandedBottomAnimationValue += node.isBottomExpanded ? StateStyles.expandedAnimationSpeed * canvas.deltaTime : -StateStyles.expandedAnimationSpeed * canvas.deltaTime;
            expandedBottomAnimationValue = Mathf.Clamp (expandedBottomAnimationValue, 0, 1);
            float bottomHeight = StateStyles.expandedHeight * expandedBottomAnimationValue;

            Rect rect = new Rect (0, y, node.size.x, bottomHeight);

            if (Event.current.IsRepaintEvent () /*&& !expandedAreaFullyClosed*/) {
                GUI.BeginClip (new Rect (0, y - borderRadius, node.size.x, bottomHeight + borderRadius));

                Rect r = new Rect (0, 0, rect.width, bottomHeight + borderRadius);

                EditorGUI.DrawSolidTexture (r, Styles.background300.color, new BorderRadius (0, 0, borderRadius, borderRadius));
                EditorGUI.DrawSolidTexture (r, Styles.outlineColor.color, new BorderWidth (Styles.outlineWidth, Styles.outlineWidth, 0, Styles.outlineWidth), new BorderRadius (0, 0, borderRadius, borderRadius));

                GUI.EndClip ();
            }

            bool drawPorts = expandedBottomAnimationValue > 0.5F;

            float paddingLeft = node.size.x / 100 * 15;
            float paddingRight = node.size.x / 100 * 15;
            float paddingBottom = 8;
            float width = node.size.x - paddingLeft - paddingRight;
            float portY = node.position.y + y + bottomHeight - paddingBottom;

            Vector2 enterPortPosition = new Vector2 (node.position.x + paddingLeft, portY);
            Vector2 updatePortPosition = new Vector2 (node.position.x + paddingLeft + width / 2, portY);
            Vector2 exitPortPosition = new Vector2 (node.position.x + node.size.x - paddingRight, portY);

            DrawConnectionPort (node.onEnterOutput, enterPortPosition, Alignment.BottomLeft, LabelPosition.Top, !drawPorts);
            DrawConnectionPort (node.onUpdateOutput, updatePortPosition, Alignment.MiddleBottom, LabelPosition.Top, !drawPorts);
            DrawConnectionPort (node.onExitOutput, exitPortPosition, Alignment.BottomRight, LabelPosition.Top, !drawPorts);

            node.onEnterOutput.isHidden = !drawExpandedPorts;
            node.onUpdateOutput.isHidden = !drawExpandedPorts;
            node.onExitOutput.isHidden = !drawExpandedPorts;

            return rect;
        }


        void UpdateHiddenSubNodes (bool isHidden) {
            foreach (Node node in node.subNodes) {
                node.isHidden = isHidden;
            }
        }


        internal override EdgePoint GetEdgePointForPort (ConnectionPort port) {
            if (port == node.onEnterOutput) {
                return new EdgePoint (node.onEnterOutput.rect.center, Vector2.down, colors.primaryColor2, colors.primaryColor1);
            }

            if (port == node.onUpdateOutput) {
                return new EdgePoint (node.onUpdateOutput.rect.center, Vector2.down, colors.primaryColor2, colors.primaryColor1);
            }

            if (port == node.onExitOutput) {
                return new EdgePoint (node.onExitOutput.rect.center, Vector2.down, colors.primaryColor2, colors.primaryColor1);
            }

            return base.GetEdgePointForPort (port);
        }


        static class StateStyles {

            public static readonly EditorColor portLabelBottomColorNoConnections = new EditorColor (new Color32 (46, 46, 46, 255), new Color32 (66, 66, 66, 255));
            public static readonly EditorColor portLabelBottomColor = new EditorColor (new Color32 (0, 99, 82, 255), new Color32 (0, 179, 162, 255));

            public static EditorColor expandedButtonBackgroundColor = new EditorColor (new Color32 (167, 167, 167, 255), new Color32 (77, 77, 77, 255));
            public static EditorColor expandedButtonIconColor = new EditorColor (new Color32 (89, 89, 89, 255), new Color32 (179, 179, 179, 255));

            public static readonly Texture2D expandibleFooterButtonIconOn;
            public static readonly Texture2D expandibleFooterButtonIconOff;

            public static readonly GUIStyle portLabelStyleOnBottomNoConnections;
            public static readonly GUIStyle portLabelStyleOnBottom;

            public static readonly Vector2 footerButtonSize = new Vector2 (34, 10);
            public static readonly Vector2 footerButtonIconSize = new Vector2 (34, 10);

            public static readonly float expandedHeight = 42;
            public static readonly float expandedAnimationSpeed = 2;


            static StateStyles () {
                expandibleFooterButtonIconOn = EditorGUIUtility.LoadAsset<Texture2D> ("Textures/btn_arrow_down.png");
                expandibleFooterButtonIconOff = EditorGUIUtility.LoadAsset<Texture2D> ("Textures/btn_arrow_up.png");

                portLabelStyleOnBottomNoConnections = new GUIStyle ();
                portLabelStyleOnBottomNoConnections.normal.textColor = portLabelBottomColorNoConnections.color;
                portLabelStyleOnBottomNoConnections.alignment = TextAnchor.MiddleCenter;
                portLabelStyleOnBottomNoConnections.fontSize = 9;
                portLabelStyleOnBottomNoConnections.font = Styles.nodeFont;

                portLabelStyleOnBottom = new GUIStyle ();
                portLabelStyleOnBottom.normal.textColor = portLabelBottomColor.color;
                portLabelStyleOnBottom.alignment = TextAnchor.MiddleCenter;
                portLabelStyleOnBottom.fontSize = 9;
                portLabelStyleOnBottom.font = Styles.nodeFont;
            }

        }

    }


}
#endif